package com.zyk.scaffold.user.controller.user;


import com.alibaba.fastjson.JSON;
import com.zyk.scaffold.core.component.CountRedisLimitComponent;
import com.zyk.scaffold.core.component.RedisSlideLimitComponent;
import com.zyk.scaffold.core.context.AppContext;
import com.zyk.scaffold.core.context.ContextHolder;
import com.zyk.scaffold.core.context.UserContext;
import com.zyk.scaffold.core.domain.Result;
import com.zyk.scaffold.common.utils.AssertUtil;
import com.zyk.scaffold.common.utils.MD5Util;
import com.zyk.scaffold.common.utils.RandomUtils;
import com.zyk.scaffold.user.dto.MailMessageDTO;
import com.zyk.scaffold.user.entity.User;
import com.zyk.scaffold.user.mail.components.MailComponent;
import com.zyk.scaffold.user.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户-用户表 前端控制器
 * </p>
 *
 * @author zhouyk
 * @since 2021-12-06
 */
@Api(tags = "用户-API")
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisSlideLimitComponent redisSlideLimitComponent;

    @Autowired
    private CountRedisLimitComponent countRedisLimitComponent;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private MailComponent mailComponent;

    @ApiOperation("用户上下文")
    @PostMapping("/context")
    public Result<String> getUserContext() {
        UserContext userContext = ContextHolder.getUserContext();
        return Result.ok(JSON.toJSONString(userContext));
    }

    @ApiOperation("APP上下文")
    @PostMapping("/app-context")
    public Result<String> getAppContext(@RequestBody HashMap request) {
        log.info("request:" + JSON.toJSONString(request));
        AppContext appContext = ContextHolder.getAppContext();
        return Result.ok(JSON.toJSONString(appContext));
    }

    @ApiOperation("用户列表")
    @GetMapping("/listUserInfos")
    public Result<List<UserInfosResp>> list(@RequestParam(required = false) String name,
                                            @RequestParam(required = false) String email) {
        List<UserInfosResp> result = userService.listUserInfos(name, email);
        return Result.ok(result);
    }

    @ApiOperation("重置验证码")
    @GetMapping("/reset-password-verification-code")
    public Result<Boolean> verificationCode(@RequestParam("email") String email){
        User user = userService.getUserByEmail(email);
        AssertUtil.notIsNull(user, "邮箱：" + email + "的用户不存在");
        String emailKey = "captcha:email:" + email;
        boolean b = redisSlideLimitComponent.tryAccess(emailKey, 1, 60);
        AssertUtil.isTrue(b, "验证码获取太频繁，请稍后再试");
        String verificationCode = RandomUtils.randomNumeric(6);
        MailMessageDTO mailMessageDTO = new MailMessageDTO();
        mailMessageDTO.setSubject("邮箱验证");
        mailMessageDTO.setTo(new String[]{email});
        mailMessageDTO.setTemplateCode("verification-code");
        HashMap<String, Object> templateData = new HashMap<>();
        templateData.put("verificationCode", verificationCode);
        mailMessageDTO.setTemplateData(templateData);
        boolean send = false;
        try {
            send = mailComponent.send(mailMessageDTO);
            if(send){
                stringRedisTemplate.opsForValue().set(emailKey, verificationCode, 10, TimeUnit.MINUTES);
            }
        } catch (Exception e) {
            log.error("验证码发送失败", e);
            redisSlideLimitComponent.release(emailKey);
            AssertUtil.error("验证码发送失败, 请稍后再试");
        }
        if(!send){
            redisSlideLimitComponent.release(emailKey);
        }
        return Result.ok(send);
    }

    @ApiOperation("重置token")
    @PostMapping("/reset-password-token")
    public Result<String> resetToken(@RequestBody @Validated PwdResetTokenReq pwdResetTokenReq){
        String emailKey = "captcha:email:" + pwdResetTokenReq.getEmail();
        String resetTokenKey = "resettoken:email:" + pwdResetTokenReq.getEmail();
        AssertUtil.isTrue(countRedisLimitComponent.check(resetTokenKey, 4),"验证码错误超过5次，请5分钟后重新尝试!");
        String verificationCode = stringRedisTemplate.opsForValue().get(emailKey);
        AssertUtil.notEmptyString(verificationCode, "验证码未发送, 或已超时");
        String target = null;
        try {
            target = MD5Util.computeMD5(verificationCode);
        } catch (NoSuchAlgorithmException e) {
            log.error("MD5计算异常", e);
        }
        AssertUtil.notEmptyString(target, "MD5计算异常, 请稍后再试");

        boolean equals = target.equals(pwdResetTokenReq.getVerificationCodeMd5());
        if (!equals) {
            this.countRedisLimitComponent.incrby(resetTokenKey, 300);
            AssertUtil.error("验证码不正确，请检查");
        }
        String resetToken = RandomUtils.getCharAndNumr(32);
        String emailResetTokenKey = "captcha:resetemail:" + resetToken;
        stringRedisTemplate.opsForValue().set(emailResetTokenKey, pwdResetTokenReq.getEmail(), 10, TimeUnit.MINUTES);
        stringRedisTemplate.delete(emailKey);
        stringRedisTemplate.delete(resetTokenKey);
        return Result.ok(null, resetToken);
    }

    @ApiOperation("修改密码")
    @PostMapping("/reset-password")
    public Result<Boolean> pwdReset(@RequestBody @Validated PwdResetRequ pwdResetRequ){
        String emailResetTokenKey = "captcha:resetemail:" + pwdResetRequ.getPwdResetToken();
        String email = stringRedisTemplate.opsForValue().get(emailResetTokenKey);
        AssertUtil.notEmptyString(email, "验证Token已超时或不存在, 请重新发送验证码");
        User user = userService.getUserByEmail(email);
        AssertUtil.notIsNull(user, "邮箱：" + email + "的用户不存在");
        UpdatePasswordRequ userEnableRequ = new UpdatePasswordRequ();
        userEnableRequ.setConfirmPassword(pwdResetRequ.getConfirmPassword());
        userEnableRequ.setNewPassword(pwdResetRequ.getNewPassword());
        Boolean aBoolean = userService.updatePassword(userEnableRequ, user.getId());
        if(aBoolean){
            stringRedisTemplate.delete(emailResetTokenKey);
            stringRedisTemplate.delete("login:lock:" + user.getAccount());
        }
        return Result.ok(aBoolean);
    }
}